#include "render_window.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <Qt/qapplication.h>

RenderWindow::RenderWindow( QWidget *p_parent )
    : QWidget( p_parent )
{
    m_timer = NULL;

    m_root = NULL;
    m_scene_manager = NULL;
    m_window = NULL;
    m_cam = NULL;

    m_text_overlay = NULL;

    m_frame_listener = NULL;

    // Make selection the default tool
    m_tool = T_SELECT;
}

RenderWindow::~RenderWindow() {
    // Ogre overlays should be destroyed before Ogre Root
    if (m_text_overlay) {
        delete m_text_overlay;
        m_text_overlay = NULL;
    }

    // Destroy Ogre Root
    if (m_root) {
        // But before that, the frame listener should be uninstalled
        if (m_frame_listener) {
            // Remove the frame listener from Ogre Root
            m_root->removeFrameListener( m_frame_listener );
            // Delete it
            delete m_frame_listener;
            m_frame_listener = NULL;
        }
        // Shut root down
        m_root->shutdown();
        // Delete the root node along with everything else
        delete m_root;
        m_root = NULL;
    }

    destroy();
}

void RenderWindow::paintEvent( QPaintEvent *p_event ) {
    resizeEvent( NULL );

    update();
}

void RenderWindow::resizeEvent( QResizeEvent *p_event ) {
    if (p_event)
        QWidget::resizeEvent( p_event );

    if (m_window) {
	    m_window->reposition( x(), y() );
	    m_window->resize( width(), height() );
	    m_window->windowMovedOrResized();
    }
}

void RenderWindow::update() {
    QWidget::update();

    if (m_root)
        m_root->renderOneFrame();
}

void RenderWindow::showEvent( QShowEvent *p_event ) {
/*    if (!m_window) {
        QApplication::flush();
//        XFlush(QX11Info::display());
        init();
    }*/

    QWidget::showEvent( p_event );
}

void RenderWindow::init() {
    init_ogre();

	// Connect a timer to the update method
	m_timer = new QTimer( this );
	QObject::connect( m_timer, SIGNAL( timeout() ), this, SLOT( update() ));
	// Reducing the timer should give a higher framerate
	m_timer->start( 40 );
}

void RenderWindow::config_ogre() {
    pm3::LOGGER *log = pm3::LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();

    if (log)
        log->report( "Init", "I: Loading Ogre configuration, log and plugins\n" );

    // Configure Ogre
	m_root = new Ogre::Root( "plugins.cfg", "ogre.cfg", "ogre.log" );
    intf->m_root = m_root;
    // Open the configuration dialog, if run for the first time
	if (!m_root->restoreConfig()) {
		if (!m_root->showConfigDialog()) {
            if (log)
                log->report( "Init", "E: Failed in both loading configuration and showing configuration dialog\n" );
			return;
        }
    }

    if (!intf->get_root()) {
        if (log)
            log->report( "Init", "I: Global interface root node pointer uninitialized\n" );
        return;
    }

    if (log)
        log->report( "Init", "I: Initializing render window\n" );

    // Initialise a rendering window
	m_window = intf->get_root()->initialise( false );
    intf->m_render_window = m_window;

    if (log)
        log->report( "Init", "I: Loading resource configuration\n" );

    // Read the resources configuration file for initial resources
	Ogre::ConfigFile config_file;

	config_file.load( "resources.cfg" );

    // Loop through the entries
	Ogre::ConfigFile::SectionIterator sec_it = config_file.getSectionIterator();

	while (sec_it.hasMoreElements()) {
		Ogre::String sec_name = sec_it.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap* settings = sec_it.getNext();

		for (Ogre::ConfigFile::SettingsMultiMap::iterator i = settings->begin(); i != settings->end(); ++i) {
			Ogre::String type_name = i->first;
			Ogre::String arch_name = i->second;

            // Add all the resource locations
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation( arch_name, type_name, sec_name );
		}
	}

    if (log)
        log->report( "Init", "I: Loading resources\n" );

    // And load them
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void RenderWindow::init_listeners() {
    pm3::LOGGER *log = pm3::LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();

    if (log)
        log->report( "Pixel", "I: Initializing listeners..\n" );

    // Create a frame listener
    if (intf->m_render_window)
	    m_frame_listener = new pm3::FRAME_LISTENER();
    // And attach it to the root node
    if (intf->m_root)
    	intf->m_root->addFrameListener( m_frame_listener );
}

void RenderWindow::init_window() {
    pm3::LOGGER *log = pm3::LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();

    if (log)
        log->report( "Pixel", "I: Initializing render window..\n" );

    Ogre::NameValuePairList params;
    QWidget *q_parent = dynamic_cast <QWidget *> ( parent() );

#ifdef Q_WS_X11
    // Get the parameters of the window QT created
    QX11Info info = x11Info();
    Ogre::String winHandle;
    winHandle  = Ogre::StringConverter::toString((unsigned long)(info.display()));
    winHandle += ":";
    winHandle += Ogre::StringConverter::toString((unsigned int)(info.screen()));
    winHandle += ":";

    assert( q_parent );

    winHandle += Ogre::StringConverter::toString( (unsigned long) q_parent->winId() );
//    winHandle += Ogre::StringConverter::toString( (unsigned long) winId() );

//    params["externalWindowHandle"] = winHandle;
    params["parentWindowHandle"] = winHandle;
#endif
#ifdef _WIN32
    if (q_parent)
        params["parentWindowHandle"] = Ogre::StringConverter::toString( ( unsigned long ) q_parent->winId() );
    else
        params["parentWindowHandle"] = Ogre::StringConverter::toString( ( unsigned long ) GetDesktopWindow );
#endif

    // Create Ogre render window
    m_window = m_root->createRenderWindow( "Render window",
                           this->width(),
                           this->height(),
                           false,
                           &params );
    intf->m_render_window = m_window;

    m_window->setActive( true );
    m_window->resize(width(), height());
    m_window->setVisible( true );

    // Get the ID of Ogre render window
	WId window_id;
    m_window->getCustomAttribute( "WINDOW", &window_id );
    assert( window_id );

    // Take over the ogre created window.
    QWidget::create( window_id );

//    resizeEvent( NULL );

    setAttribute( Qt::WA_PaintOnScreen );
/*    setAttribute( Qt::WA_NoSystemBackground );
    setAttribute( Qt::WA_NoBackground );
    setAttribute( Qt::WA_NativeWindow );*/
    setAttribute( Qt::WA_OpaquePaintEvent );
}

void RenderWindow::init_ogre() {
    pm3::LOGGER *log = pm3::LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();
    pm3::PIXELWORLD *world = pm3::PIXELWORLD::getSingletonPtr();
    pm3::PHYSICS *physics = pm3::PHYSICS::getSingletonPtr();

    // Time-seeded random
    srand( time( NULL ) );

    // Configure Ogre
    config_ogre();
    // Init window
    init_window();
    // Init listeners
    init_listeners();

    if (log)
        log->report( "Pixel", "I: Initializing world..\n" );

    // Create a portal connected zone scene manager
    m_scene_manager = (Ogre::PCZSceneManager *) intf->get_root()->createSceneManager( "PCZSceneManager", "PCZSceneManager" );
    intf->m_scene_mgr = m_scene_manager;
    m_scene_manager->init( "ZoneType_Default" );

    // Initialize physics
    physics->init();
    // Initialize the world
    world->init();
    // Update physics debug node
    physics->get_physics_debug()->setNode( pm3::INTERFACE::getSingleton().get_world_node() );

    // Create the text overlay
    m_text_overlay = new pm3::hud::TEXT_OVERLAY();

    // Add a camera orientation and position textbox
    pm3::hud::TEXT_OVERLAY::getSingleton().add_textbox( "Camera", "<Camera info>", 10, 10, 100, 20, Ogre::ColourValue::Green );
    // Add an FPS textbox
    pm3::hud::TEXT_OVERLAY::getSingleton().add_textbox( "FPS", "<Frames per second>", 10, 20, 100, 40, Ogre::ColourValue::Green );

    // Update the window
    m_window->update();
}

void RenderWindow::set_paint_color( const Ogre::ColourValue &r_color ) {
    m_color = r_color;
}

void RenderWindow::set_tool( TOOL e_tool ) {
    m_tool = e_tool;
}

Ogre::Vector3 RenderWindow::pick_grid( int i_x, int i_y, int i_z ) {
    pm3::PIXELWORLD *world = pm3::PIXELWORLD::getSingletonPtr();
    std::pair< bool, Ogre::Real > intersection;
    Ogre::Ray ray;
    Ogre::Plane plane;
    Ogre::Vector2 v;
    Ogre::Vector3 p;
    float halfheight = 0.0f;

    halfheight = world->m_map.get_room_size() / 2.0f;

    // Get normalized screen coordinates
    v.x = i_x; v.x /= width();
    v.y = i_z; v.y /= height();

    // Define ray
    ray = world->m_player.get_camera()->getCameraToViewportRay( v.x, v.y );
    // Define plane
    plane.redefine( Ogre::Vector3( 0, 1, 0 ), Ogre::Vector3( 0, i_y, 0 ) );

    // Find the intersection
    intersection = ray.intersects( plane );
    // Intersected - well, it should have..
    if (intersection.first) {
        // Get intersection point
        p = ray.getPoint( intersection.second );
        // Convert back to grid coordinates
        p = world->m_map.world_to_grid( p );
    }

    return p;
}

void RenderWindow::mousePressEvent( QMouseEvent *p_event ) {
    pm3::PIXELWORLD *world = pm3::PIXELWORLD::getSingletonPtr();
    pm3::PIXELROOM *room = NULL;
    Ogre::Vector3 p;

    if (p_event && m_frame_listener) {
        m_frame_listener->m_mouse_state = p_event->buttons();

        m_frame_listener->m_mouse_old_pos.x = p_event->x();
        m_frame_listener->m_mouse_old_pos.y = p_event->y();

        // Left click?
        if (( p_event->buttons() & Qt::LeftButton ) != 0) {
            // Make sure there's a layer to draw on
            if (!pm3::lua::g_active_layer)
                return;

            // Pick a point on the grid
            p = pick_grid( p_event->x(), pm3::lua::g_active_layer->get_id(), p_event->y() );

            // Are we drawing pixels?
            if (m_tool == T_DRAW) {
                // Set the pixel at the given coordinates
                world->m_map.set_pixel( p, m_color );

            // Are we selecting pixels?
            } else if (m_tool == T_SELECT) {
                // Emit a roompick signal at the given coordinates
                room = world->m_map.get_room( p.y, p.x, p.z );
                emit room_picked( room );
            }
        }
    }
}

void RenderWindow::mouseMoveEvent( QMouseEvent *p_event ) {
    if (p_event && m_frame_listener) {
        m_frame_listener->m_mouse_new_pos.x = p_event->x();
        m_frame_listener->m_mouse_new_pos.y = p_event->y();
    }
}

void RenderWindow::mouseReleaseEvent( QMouseEvent *p_event ) {
    if (p_event && m_frame_listener) {
        m_frame_listener->m_mouse_state = p_event->buttons();
    }
}

